package com.biz.primus.ms.member.service;

import com.biz.primus.base.enums.ChannelTypes;
import com.biz.primus.common.utils.StringUtil;
import com.biz.primus.model.member.exception.UserException;
import com.biz.primus.model.member.exception.UserExceptionType;
import com.biz.primus.model.member.enums.MemberTypes;
import com.biz.primus.model.member.enums.SexTypes;
import com.biz.primus.model.member.vo.MemberLevelVo;
import com.biz.primus.model.member.vo.request.*;
import com.biz.primus.model.member.vo.response.BaseResponseVo;
import com.biz.primus.model.member.vo.response.MemberLoginResponseVo;
import com.biz.primus.model.member.vo.response.MemberRegisterResponseVo;
import com.biz.primus.model.misc.enums.SMSType;
import com.biz.primus.ms.base.service.AbstractBaseService;
import com.biz.primus.ms.member.dao.po.Member;
import com.biz.primus.ms.member.dao.redis.MemberAuthRedisDao;
import com.biz.primus.ms.member.dao.redis.MemberRedisDao;
import com.biz.primus.ms.member.dao.repository.MemberRepository;
import com.biz.primus.ms.member.dao.ro.MemberRo;
import com.biz.primus.ms.member.event.LoginEvent;
import com.biz.primus.ms.member.feign.MiscFeignClient;
import com.biz.primus.ms.member.trans.MemberToLoginResp;
import com.biz.primus.ms.member.trans.MemberToMemberRo;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * 会员
 * @author jun.liu
 * @date 2017年10月23日
 * @reviewer
 * @see
 */
@Slf4j
@Service
public class MemberService extends AbstractBaseService
{

    private final MemberRedisDao memberRedisDao;

    private final MemberRepository memberRepository;

    private final MemberAuthRedisDao memberAuthRedisDao;

    private final MemberLevelService memberLevelService;

    private final MemberAuthTokenService memberAuthTokenService;

    private final MiscFeignClient miscFeignClient;


    @Autowired
    public MemberService(MemberRedisDao memberRedisDao, MemberRepository memberRepository,
                         MemberAuthRedisDao memberAuthRedisDao,
                         MemberLevelService memberLevelService, MemberAuthTokenService memberAuthTokenService,
                         MiscFeignClient miscFeignClient)
    {
        this.memberRedisDao = memberRedisDao;
        this.memberRepository = memberRepository;
        this.memberAuthRedisDao = memberAuthRedisDao;
        this.miscFeignClient = miscFeignClient;
        this.memberLevelService = memberLevelService;
        this.memberAuthTokenService = memberAuthTokenService;
    }


    /**
     * 客服中心手机注册
     * @author jun.liu
     * @param mobile
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Boolean registerByMobile(String mobile)
    {
        log.debug("注册参数信息:{}", mobile);

        if (StringUtils.isBlank(mobile))
        {
            throw new UserException(UserExceptionType.MOBILE_NOT_EXIST);
        }

        //将账号默认设置为绑定的手机
        if(StringUtils.isNotBlank(mobile))
        {
            if (!StringUtil.isMobileValid(mobile))
            {
                //注册绑定的手机号格式不正确
                throw new UserException(UserExceptionType.MOBILE_ERROR);
            }
            //验证账号mobile是否已存在
            MemberRo ro = memberRedisDao.findByMobile(mobile);
            if (ro != null) {
                throw new UserException(UserExceptionType.USER_ALREADY_EXIST);
            }
        }
        try {
            //密码 默认为手机号后六位
            String password = mobile.substring(5);

            MemberLevelVo minMemberLevel = memberLevelService.findByIdOnPo(null);
            Member po = new Member();
            po.setAccount(mobile);
            po.setChannelType(ChannelTypes.CUSTOMERCENTER);
            po.setMobile(mobile);
            po.setRegTime(LocalDateTime.now());
            setPassword(password, po);

            po.setMemberLevelId(minMemberLevel.getId());
            po.setMemberType(MemberTypes.cside);

            saveOrUpdateUsingPoPKIncreament(memberRepository, memberRedisDao, po, new MemberToMemberRo());


            publishEvent(new LoginEvent(this,po.getId()));
            //todo 注册成功是否发布事件？是否还要发布注册成功短信
        }catch (Exception e){
            log.error(e.getMessage());
            return false;
        }

        return true;
    }


    /**
     * 账号注册
     * @author jun.liu
     * @param reqVo
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public MemberRegisterResponseVo register(MemberRegisterRequestVo reqVo)
    {
        log.debug("注册参数信息:{}", reqVo);
        if (reqVo == null)
        {
            throw new UserException(UserExceptionType.PARAMS_ERROR);
        }
        String account = reqVo.getAccount();
        String mobile = reqVo.getMobile();
        String email = reqVo.getEmail();
        String smsCode = reqVo.getSmsCode();
        String password = reqVo.getPassword();
        ChannelTypes channelCode = reqVo.getChannelCode();

        if (StringUtils.isBlank(mobile))
        {
            throw new UserException(UserExceptionType.MOBILE_NOT_EXIST);
        }

        if (!StringUtil.isMobileValid(mobile))
        {
            //注册绑定的手机号格式不正确
            throw new UserException(UserExceptionType.MOBILE_ERROR);
        }

        if (StringUtils.isNotBlank(email) && !StringUtil.isEmail(email))
        {
            //注册绑定的邮箱格式不正确
            throw new UserException(UserExceptionType.EMAIL_ERROR);
        }

        //如果name为空 则证明不是自有账号 则进行电话 邮箱赋值
        if (StringUtils.isBlank(account))
        {
            //将账号默认设置为绑定的手机
            if(StringUtils.isNotBlank(mobile))
            {
                //验证账号mobile是否已存在
                MemberRo ro = memberRedisDao.findByMobile(mobile);
                if (ro != null) {
                    throw new UserException(UserExceptionType.MOBILE_EXISTED);
                }
                account = mobile;
                if (StringUtils.isNotBlank(email)) {
                    //验证账号email是否已存在
                    MemberRo emailMember = memberRedisDao.findByEmail(email);
                    if (emailMember != null) {
                        throw new UserException(UserExceptionType.EMAIL_EXISTED);
                    }
                }
            }//将账号默认设置为绑定的email号码
            else if(StringUtils.isBlank(mobile) && StringUtils.isNotBlank(email))
            {
                //验证账号email是否已存在
                MemberRo ro = memberRedisDao.findByEmail(email);
                if (ro != null) {
                    throw new UserException(UserExceptionType.EMAIL_EXISTED);
                }
                account = email;
            }
        }
        else
        {
            //验证账号account是否已存在
            MemberRo ro = memberRedisDao.findByAccount(account);
            if (ro != null) {
                throw new UserException(UserExceptionType.USER_ALREADY_EXIST);
            }
        }

        if (StringUtils.isBlank(password))
        {
            //若没传密码 默认为手机号后六位
            password = mobile.substring(5);
        }
        if (channelCode==null)
        {
            throw new UserException(UserExceptionType.CHANNEL_CODE_NOT_EXIST);
        }
        if (StringUtils.isBlank(smsCode))
        {
            throw new UserException(UserExceptionType.SMSCODE_NOT_EXIST);
        }

        //验证短信验证码
        // todo 连调短信的时候 启动短信服务 打开此处注释
//        Boolean valid = miscFeignClient.validateAndDisableSMSCode(mobile, SMSType.REGISTER, smsCode);
//        if (!valid)
//        {
//            throw new UserException(UserExceptions.SMSCODE_ERROR);
//        }



        MemberLevelVo minMemberLevel = memberLevelService.findByIdOnPo(null);
        Member po = new Member();
        po.setAccount(account);
        po.setChannelType(channelCode);
        po.setMobile(mobile);
        po.setEmail(email);
        po.setIdCardName(reqVo.getIdCardName());
        po.setNickName(reqVo.getNickName());
        po.setRegDept(reqVo.getRegDept());
        po.setRegIp(reqVo.getRegIp());
        po.setRegTime(LocalDateTime.now());
        po.setRegAccountSrc(reqVo.getRegAccountSrc());

        setPassword(password, po);

        po.setMemberLevelId(minMemberLevel.getId());
        po.setMemberType(MemberTypes.cside);

        saveOrUpdateUsingPoPKIncreament(memberRepository, memberRedisDao, po, new MemberToMemberRo());


        //todo 注册成功是否发布事件？是否还要发布注册成功短信
        publishEvent(new LoginEvent(this,po.getId()));
        return new MemberRegisterResponseVo(po.getId(), po.getMobile(), minMemberLevel.getLevel(), minMemberLevel.getLevelDesc(), po.getOriginalPassword());
    }

    /**
     * 账号登录
     * @author jun.liu
     * @param reqVo
     * @return
     */
    public MemberLoginResponseVo login(MemberLoginRequestVo reqVo, Boolean enableAuthToken)
    {
        log.debug("注册参数信息:{}", reqVo);
        if (reqVo == null)
        {
            throw new UserException(UserExceptionType.PARAMS_ERROR);
        }
        if (StringUtils.isBlank(reqVo.getAccount())) {
            throw new UserException(UserExceptionType.USERNAME_NOT_EXIST);
        }
        if (StringUtils.isBlank(reqVo.getPassword())) {
            throw new UserException(UserExceptionType.PASSWORD_NOT_EXIST);
        }
        String account = reqVo.getAccount();
        String password = reqVo.getPassword();
        MemberRo ro = this.memberRedisDao.findByAccount(account);
        if(ro == null)
        {
            if (StringUtil.isMobileValid(account))
            {
                ro = this.memberRedisDao.findByMobile(account);
            }
            else if (StringUtil.isEmail(account))
            {
                ro = this.memberRedisDao.findByEmail(account);
            }
        }
        if(ro == null)
        {
            throw new UserException(UserExceptionType.USERNAME_OR_PASSWORD_ERROR);
        }

        //密码校验
        boolean flag = Objects.equals(generatePasswordThreadSafe(password, ro.getSalt()), ro.getPassword());
        if(!flag)
        {
            throw new UserException(UserExceptionType.OLD_PASSWORD_ERROR);
        }

        //todo 登录成功是否发布事件？是否要设置最后登录信息？
        //loginSource.loginResponse().send(MessageBuilder.withPayload(ro.getId()).build());
        Member member = new Member();
        BeanUtils.copyProperties(ro,member);
        return loginSucessResponseVo(enableAuthToken,member);
    }

    /**
     * 手机号验证码登录 没账号则注册 默认密码手机号后六位
     * @param reqVo
     * @return
     */
    public MemberLoginResponseVo smsCodeLogin(MemberSmsCodeLoginRequestVo reqVo, Boolean enableAuthToken)
    {

        log.debug("注册参数信息:{}", reqVo);
        if (reqVo == null)
        {
            throw new UserException(UserExceptionType.PARAMS_ERROR);
        }
        String mobile = reqVo.getMobile();
        String smsCode = reqVo.getSmsCode();

        //验证短信验证码
        // todo 连调短信的时候 启动短信服务 打开此处注释
//        Boolean valid = miscFeignClient.validateAndDisableSMSCode(mobile, SMSType.QUICKLOGIN, smsCode);
//        if (!valid)
//        {
//            throw new UserException(UserExceptions.SMSCODE_ERROR);
//        }

        MemberRo ro = this.memberRedisDao.findByMobile(mobile);
        if(ro == null)
        {
            ro = this.memberRedisDao.findByAccount(mobile);
        }
        if(ro == null)
        {
            MemberRegisterRequestVo vo = new MemberRegisterRequestVo();
            vo.setAccount(mobile);
            vo.setMobile(mobile);
            vo.setSmsCode(smsCode);
            vo.setChannelCode(reqVo.getChannelCode());
            this.register(vo);
            ro = this.memberRedisDao.findByMobile(mobile);
        }

        //todo 登录成功是否发布事件？是否要设置最后登录信息？
        Member po = new Member();
        BeanUtils.copyProperties(ro,po);
        return loginSucessResponseVo(enableAuthToken,po);
    }

    /**
     * 自动登录
     * @param reqVo
     * @param enableAuthToken
     * @return
     */
    public MemberLoginResponseVo autologin(BaseRequestVo reqVo, Boolean enableAuthToken)
    {
        log.debug("注册参数信息:{}", reqVo);
        if (reqVo == null)
        {
            throw new UserException(UserExceptionType.PARAMS_ERROR);
        }
        Long memberId = reqVo.getGp().getMemberId();
        Member po = this.memberRepository.findOne(memberId);
        if(po == null)
        {
            throw new UserException(UserExceptionType.USER_NOT_EXIST);
        }

        //todo 登录成功是否发布事件？是否要设置最后登录信息？

        return loginSucessResponseVo(enableAuthToken,po);
    }

    /**
     * 返回值组装
     * @param enableAuthToken
     * @param member
     * @return
     */
    @NotNull
    private MemberLoginResponseVo loginSucessResponseVo(Boolean enableAuthToken,Member member) {
        //生成登录认证token
        String token = "";
        if (enableAuthToken) {
            token = memberAuthTokenService.generateNewToken(member.getId());
        }

        //生成登录响应数据
        //个人信息
        MemberLoginResponseVo resp = new MemberToLoginResp().apply(member);
        resp.setAutoToken(token);
        //会员等级
        MemberLevelVo memberLevelVo = memberLevelService.findByIdOnPo(member.getMemberLevelId());
        resp.setMemberLevel(memberLevelVo.getLevel());
        //优惠券数量 todo
        //电子钱包金额 todo
        return resp;
    }

    /**
     * 修改密码
     * @param reqVo
     */
    @Transactional(rollbackFor = Exception.class)
    public void changePassword(MemberChangePasswordRequestVo reqVo)
    {
        if (reqVo == null) {
            throw new UserException(UserExceptionType.PARAMS_ERROR);
        }

        Long memberId = reqVo.getMemberId();
        String oldPassword = reqVo.getOldPassword();
        String newPassword = reqVo.getNewPassword();

        if (memberId == null || memberId == 0L) {
            throw new UserException(UserExceptionType.MEMBERID_NULL);
        }
        if (StringUtils.isBlank(oldPassword)) {
            throw new UserException(UserExceptionType.PASSWORD_NOT_EXIST);
        }
        if (StringUtils.isBlank(newPassword)) {
            throw new UserException(UserExceptionType.PASSWORD_NOT_EXIST);
        }
        if (StringUtils.equals(oldPassword, newPassword)) {
            throw new UserException(UserExceptionType.NEW_PASSWORD_REPEATED);
        }

        Member po = this.memberRepository.findOne(memberId);
        if(po == null)
        {
            throw new UserException(UserExceptionType.USER_NOT_EXIST);
        }
        //密码校验
        boolean flag = Objects.equals(generatePasswordThreadSafe(oldPassword, po.getSalt()), po.getPassword());
        if(!flag)
        {
            throw new UserException(UserExceptionType.OLD_PASSWORD_ERROR);
        }

        setPassword(newPassword, po);

        //todo 修改密码成功是否发布事件？是否要设置最后登录信息？

        saveOrUpdateUsingPoPKIncreament(memberRepository, memberRedisDao, po, new MemberToMemberRo());

    }

    /**
     * 重置密码
     * @param reqVo
     */
    @Transactional(rollbackFor = Exception.class)
    public void resetPassword(MemberResetPasswordRequestVo reqVo)
    {
        if (reqVo == null) {
            throw new UserException(UserExceptionType.PARAMS_ERROR);
        }

        String account = reqVo.getMobile();
        String smsCode = reqVo.getSmsCode();
        String newPassword = reqVo.getNewPassword();

        if (StringUtils.isBlank(account))
        {
            throw new UserException(UserExceptionType.MOBILE_NOT_EXIST);
        }
        if (StringUtils.isBlank(smsCode))
        {
            throw new UserException(UserExceptionType.SMSCODE_NOT_EXIST);
        }
        if (StringUtils.isBlank(newPassword))
        {
            throw new UserException(UserExceptionType.PASSWORD_NOT_EXIST);
        }

        Member po = this.memberRepository.findByAccount(account);
        if(po == null)
        {
            po = this.memberRepository.findByMobile(account);
            if(po == null)
            {
                throw new UserException(UserExceptionType.USER_NOT_EXIST);
            }
        }

        //验证短信验证码
        Boolean valid = miscFeignClient.validateAndDisableSMSCode(po.getMobile(), SMSType.FORGOT_PASSWORD, smsCode);
        if (!valid)
        {
            throw new UserException(UserExceptionType.SMSCODE_ERROR);
        }

        setPassword(newPassword, po);

        //todo 重置密码成功是否发布事件？是否要设置最后登录信息？

        saveOrUpdateUsingPoPKIncreament(memberRepository, memberRedisDao, po, new MemberToMemberRo());
    }

    /**
     * 设置新密码
     * @param password
     * @param po
     */
    @Transactional(rollbackFor = Exception.class)
    void setPassword(String password, Member po) {
        String newSalt;
        do {
            newSalt = this.generateSalt();
        } while (StringUtils.isBlank(newSalt) || Objects.equals(po.getSalt(), newSalt));

        po.setSalt(newSalt);

        String newRawPassword = this.generatePasswordThreadSafe(password, newSalt);
        po.setPassword(newRawPassword);
        po.setOriginalPassword(password);
    }

    /**
     * 根据id查询MemberLoginResponseVo对象
     * @param id
     * @return
     */
    public MemberLoginResponseVo findMemberLoginResponseVoById(Long id, Boolean enableAuthToken)
    {
        Member po = this.findPoById(id);
        return loginSucessResponseVo(enableAuthToken,po);
    }

    /**
     * 更新个人信息(生日,性别,昵称
     * @param reqVo
     */
    @Transactional(rollbackFor = Exception.class)
    public void updatePersonalInfo(UpdateMemberPersonalInfoRequestVo reqVo)
    {
        if (reqVo == null) {
            throw new UserException(UserExceptionType.PARAMS_ERROR);
        }

        Member po = this.memberRepository.findOne(reqVo.getGp().getMemberId());
        if(po == null)
        {
            throw new UserException(UserExceptionType.USER_NOT_EXIST);
        }

        if(reqVo!=null){
            po.setSexType(reqVo.getSex());
        }else{
            po.setSexType(SexTypes.secret);
        }
        if(reqVo.getBirthday()!=null)
        {
            po.setBirthday(reqVo.getBirthday());
        }
        if(StringUtils.isNotBlank(reqVo.getIdCardName()))
        {
            po.setIdCardName(reqVo.getIdCardName());
        }
        if(StringUtils.isNotBlank(reqVo.getNickName()))
        {
            po.setNickName(reqVo.getNickName());
        }
        if(StringUtils.isNotBlank(reqVo.getMobile()))
        {
            po.setMobile(reqVo.getMobile());
        }
        if(StringUtils.isNotBlank(reqVo.getEmail()))
        {
            po.setEmail(reqVo.getEmail());
        }
        if(StringUtils.isNotBlank(reqVo.getPortraitUrl()))
        {
            po.setPortraitUrl(reqVo.getPortraitUrl());
        }

        //todo 更新个人信息成功是否发布事件？

        saveOrUpdateUsingPoPKIncreament(memberRepository, memberRedisDao, po, new MemberToMemberRo());
    }

    /**
     *根据账号匹配会员信息
     */
    public MemberRo findRoByAccount(String account)
    {
        return this.memberRedisDao.findByAccount(account);
    }

    /**
     *根据账号匹配会员信息
     */
    public MemberRo findRoByMobile(String mobile)
    {
        return this.memberRedisDao.findByMobile(mobile);
    }

    /**
     *根据账号匹配会员信息
     */
    public MemberRo findRoByEmail(String email)
    {
        return this.memberRedisDao.findByEmail(email);
    }

    /**
     * 根据id查询po对象
     * @param id
     * @return
     */
    public Member findPoById(Long id) {
        if (id == null || id == 0L) {
            throw new UserException(UserExceptionType.ID_ERROR);
        }
        Member po = this.memberRepository.findOne(id);
        if (po == null) {
            throw new UserException(UserExceptionType.USER_NOT_EXIST);
        }
        return po;
    }

    /**
     * 根据id查询ro对象
     * @param id
     * @return
     */
    public MemberRo findRoById(Long id) {
        if (id == null || id == 0L) {
            throw new UserException(UserExceptionType.ID_ERROR);
        }
        MemberRo ro = this.memberRedisDao.findOne(id);
        if (ro == null) {
            throw new UserException(UserExceptionType.USER_NOT_EXIST);
        }
        return ro;
    }

    /**
     * 获取会员总数量
     * @return
     */
    public Long findMemberCount()
    {
        return this.memberRedisDao.findMemberCount();
    }

    /**
     * 分页查询用户基本信息
     * @return
     */
    public List<BaseResponseVo> findBaseInfoByPageable(BasePageableRequestVo reqVo)
    {
        if (reqVo == null) {
            return null;
        }
        List<Long> ids = memberRedisDao.findIdsByPageable(reqVo.getPage(), reqVo.getSize());
        return CollectionUtils.isEmpty(ids) ? Collections.<BaseResponseVo>emptyList() : Lists.transform(ids, new Function<Long, BaseResponseVo>() {
            @Override
            public BaseResponseVo apply(Long input) {
                return new BaseResponseVo(reqVo.getGp().getMemberId());
            }
        });
    }

    /**
     * 线程安全的MD5加密
     */
    public String generatePasswordThreadSafe(String oriPassword, String salt) {
        String password = String.format("%s%s", oriPassword, salt);
        if (StringUtils.isBlank(password)) {
            return null;
        }
        return DigestUtils.md5Hex(password);
    }

    /**
     * 生产盐值
     * @return
     */
    public String generateSalt() {
        // 生成salt
        return org.codelogger.utils.StringUtils.getRandomPasswordString(16);
    }





}
